Django拓展auth用户

Django拓展auth 用户

完成注册登录登出注销操作

!!!拓展auth用户请务必在 python manage.py migrate 前进行!!!

  1. 新建一个project:closetUsers

  2. 修改 settings.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #closetUsers/settings.py
    INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users', # modify
    'rest_framework', # modify
    'django_filters', # modify
    ]
    #update database config
    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'closetUser',
    'USER': 'may',
    'PASSWORD': 'may233',
    'HOST': '120.76.62.132',
    'PORT': '3306',
    }
    }
    #add auth_user_model !after! add the extended user
    AUTH_USER_MODEL = 'users.User' #pattern: <appName>.<className>
  3. 新建一个app:users

  4. 修改 users/__init__.py

    1
    2
    3
    4
    #users/__init__.py
    import pymysql
    pymysql.install_as_MySQLdb()
    #use for django2.x version to link mysql database
  5. users底下添加文件 urls.py ,修改 closetUsers.urls.py

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #closetUsers/urls.py
    from django.contrib import admin
    from django.urls import path
    from django.conf.urls import include #add this

    urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('users.urls')),
    #add this, pattern: <appName>.urls
    ]
  6. 修改 users.models.py, 添加拓展的用户类,继承自 AbstractUser, 注:如原来的auth.User 已有的属性不需要再添加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #users/models.py
    from django.db import models
    from django.contrib.auth.models import AbstractUser


    # Create your models here.
    class User(AbstractUser):
    """
    save the user info
    """
    style = models.CharField(max_length=30, default='casual', null=True, blank=True)
    profile = models.URLField(default='http://120.76.62.132:8080/photos/40padded.jpg', blank=True)
    phone = models.CharField(max_length=11, null=True, blank=True)

    def __str__(self):
    return self.username
  7. 建表

    1
    2
    3
    xxxdeMacBook-Pro:closetUsers xxx$ python3 manage.py migrate
    xxxdeMacBook-Pro:closetUsers xxx$ python3 manage.py makemigrations users
    xxxdeMacBook-Pro:closetUsers xxx$ python3 manage.py migrate users
  8. 重写UserCreationForm, UserChangeForm, 在users 底下新建 form.py文件,其实我只是想要提供一个restful的API而不是建立一个网站,因而应该不需要使用到表单创建,但Django文档中说明拓展User需要重写这两个表单因此还是重写了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #users/forms.py
    from django.contrib.auth.forms import UserCreationForm, UserChangeForm
    from django import forms
    from .models import User


    class RegisterForm(UserCreationForm):

    class Meta(UserCreationForm.Meta):
    model = User
    fields = ('username', 'email', 'phone', 'style', 'profile')

    # check if email is valid
    def clean_email(self):
    email = self.cleaned_data['email']
    users = User.objects.filter(email=email)
    if users:
    raise forms.ValidationError("该邮箱已注册过,尝试登录?")
    return email


    class ChangeInfoForm(UserChangeForm):

    class Meta(UserChangeForm.Meta):
    model = User
    fields = ('username', 'email', 'phone', 'style', 'profile')

一、注册

法一:暴力解决

  1. 在 views.py中添加相关类或函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #users/views.py
    from django.shortcuts import render
    from django.http import JsonResponse, HttpResponse
    from rest_framework import status
    from .models import User


    # Create your views here.
    def sign_up(request):
    username = request.data['username']
    password = request.data['password']
    users = User.objects.filter(username=username)
    if users:
    return HttpResponse('该用户已存在', status=status.HTTP_400_BAD_REQUEST)
    user = User.objects.create_user(username=username, password=password)
    return HttpResponse('创建成功,登录?', status=status.HTTP_200_OK)
  2. 在 urls.py 中添加相关路径

    1
    2
    3
    4
    5
    6
    7
    8
    #users/urls.py
    from django.urls import path
    from rest_framework.urlpatterns import format_suffix_patterns
    from users import views

    urlpatterns = format_suffix_patterns([
    path('users/add1/', views.sign_up),
    ])
  3. 使用 httpie 模拟post请求

    1
    2
    3
    #pattern: http post <urlAddress> @<jsonFileAddress>
    xxxdeMacBook-Pro:closetUsers xxx$ http --json post http://127.0.0.1:8000/users/add1/ @/Users/xxx/Desktop/closet/jsonTest/login.json
    HTTP/1.1 403 Forbidden

    Error: HTTP/1.1 403 Forbidden, CSRF verification failed. Request aborted.

    Solve: 为了防止跨站伪造请求,django引入了csrf机制,引入csrf_exempt

    1
    2
    3
    4
    5
    6
    #users/views.py
    from django.views.decorators.csrf import csrf_exempt

    @csrf_exempt
    def sign_up(request):
    pass

    Error: HTTP/1.1 500 Internal Server Error,继续看报错信息,发现进行sign_up函数解析后出现错误,发现是request以json格式发送请求时,无法以request.data['username'] 的方式获取数据

    Solve:

    1
    2
    3
    4
    5
    6
    7
    #users/views.py  inside sign_up function 
    import json
    post_body = request.body
    #django2.x use body, while django1.x use raw_post_data
    data = json.loads(post_body)
    username = data['username']
    password = data['password']

    再次尝试使用httpie发送请求,成功!开心!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    xxxdeMacBook-Pro:closetUsers xxx$ http --json post http://127.0.0.1:8000/users/add1/ @/Users/xxx/Desktop/closet/jsonTest/login.json
    HTTP/1.1 200 OK
    Content-Length: 24
    Content-Type: text/html; charset=utf-8
    Date: Sat, 22 Dec 2018 05:17:42 GMT
    Server: WSGIServer/0.2 CPython/3.7.0
    X-Frame-Options: SAMEORIGIN

    创建成功,登录?

    xxxdeMacBook-Pro:closetUsers xxx$
  4. 以上只是简单的注册,下面我们来拓展一下,加上对于信息的验证,加上了对于手机号码和邮箱的格式验证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    #users/views.py
    from django.http import JsonResponse, HttpResponse
    from rest_framework import status
    from .models import User
    from django.views.decorators.csrf import csrf_exempt
    import json
    from django.contrib.auth import login, authenticate, logout
    import re


    # Create your views here.
    @csrf_exempt
    def sign_up(request):

    if request.method == 'POST':
    post_body = request.body
    data = json.loads(post_body)
    if 'username' in data:
    username = data['username']
    else:
    username = 'may'
    password = data['password']
    if 'email' in data:
    email = data['email']
    # check if email is valid
    email_pat = re.compile(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$')
    if not re.match(email_pat, email):
    return JsonResponse(data={"msg": "请输入正确的邮箱名"}, status=status.HTTP_400_BAD_REQUEST)
    else:
    email = 'error'
    if 'phone' in data:
    phone = data['phone']
    # check if phone is valid
    phone_pat = re.compile(r'^(13\d|14[5|7]|15\d|166|17[3|6|7]|18\d)\d{8}$')
    if not re.match(phone_pat, phone):
    return JsonResponse(data={"msg": "请输入正确的手机号"}, status=status.HTTP_400_BAD_REQUEST)
    else:
    phone = 'error'
    if 'profile' in data:
    profile = data['profile']
    else:
    profile = 'http://120.76.62.132:8080/photos/default.jpg'
    if 'style' in data:
    style = data['style']
    else:
    style = 'casual'
    # users = User.objects.filter(username=username)
    email_users = User.objects.filter(email=email)
    phone_users = User.objects.filter(phone=phone)
    name_users = User.objects.filter(username=username)
    if email_users:
    return JsonResponse(data={"msg": "该邮箱已注册过"}, status=status.HTTP_400_BAD_REQUEST)
    if phone_users:
    return JsonResponse(data={"msg": "该手机号已注册过"}, status=status.HTTP_400_BAD_REQUEST)
    if name_users:
    return JsonResponse(data={"msg": "该用户已存在"}, status=status.HTTP_400_BAD_REQUEST)
    if email == 'error':
    email = ''
    if phone == 'error':
    phone = ''
    user = User.objects.create_user(username=username, password=password, email=email,
    phone=phone, style=style, profile=profile)
    return JsonResponse(data={"msg": "创建成功,登录?"}, status=status.HTTP_200_OK)

法二:django-rest-framework

尝试用django-rest-framework提供的方法实现

二、登录

法一:沿袭我们的暴力方案

2.1 使用auth模块提供的login

使用username和password登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#users/views.py
@csrf_exempt
def sign_in(request):

if request.method == 'POST':
post_body = request.body
data = json.loads(post_body)
username = data['username']
password = data['password']
print(username)
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request, user)
print(request.user)
return JsonResponse(data={"msg": "OK"}, status=status.HTTP_200_OK)
else:
return JsonResponse(data={"msg": "用户不存在"}, status=status.HTTP_400_BAD_REQUEST)
else:
return JsonResponse(data={"msg": "用户或密码错误"}, status=status.HTTP_400_BAD_REQUEST)

2.2 拓展,自定义后端,使用邮箱登录

参考自定义认证后台

  1. 添加 backends.py文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    from .models import User

    class EmailBackend(object):

    def authenticate(self, request, **credentials):
    # 要注意登录表单中用户输入的用户名或者邮箱的 field 名均为 username
    email = credentials.get('email', credentials.get('username'))
    try:
    user = User.objects.get(email=email)
    except User.DoesNotExist:
    pass
    else:
    if user.check_password(credentials["password"]):
    return user

    def get_user(self, user_id):
    """
    该方法是必须的
    """
    try:
    return User.objects.get(pk=user_id)
    except User.DoesNotExist:
    return None
  2. 在 setttings.py 中加入后端文件告诉Django 你使用了自定义后端

    1
    2
    3
    4
    5
    AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'users.backends.EmailBackend',
    'users.backends.PhoneBackend',
    )

2.3 同理拓展,自定义后端,使用手机登录

代码大同小异,不赘述了

三、登出

法一:继续,暴力解决一切事物

1
2
3
4
5
6
7
#users/views.py
@csrf_exempt
def sign_out(request):

if request.method == 'GET':
logout(request)
return JsonResponse(data={"msg": "登出成功"}, status=status.HTTP_200_OK)

四、注销

法一:继续,暴力解决一切事物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#users/views.py
@csrf_exempt
def sign_off(request):

if request.method == 'POST':
post_body = request.body
data = json.loads(post_body)
username = data['username']
password = data['password']
user = authenticate(username=username, password=password)
if user:
email_pat = re.compile(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$')
phone_pat = re.compile(r'^(13\d|14[5|7]|15\d|166|17[3|6|7]|18\d)\d{8}$')
if re.match(email_pat, username):
delete_result = User.objects.filter(email=username).delete()
elif re.match(phone_pat, username):
delete_result = User.objects.filter(phone=username).delete()
else:
delete_result = User.objects.filter(username=username).delete()
if delete_result:
return JsonResponse(data={"msg": "注销成功"}, status=status.HTTP_200_OK)
return JsonResponse(data={"msg": "用户不存在"}, status=status.HTTP_400_BAD_REQUEST)
给咱来个🍰,啾咪